package org.apache.hadoop.hdfs.protocol.datatransfer;

import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.hdfs.StorageType;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.apache.hadoop.hdfs.protocol.ExtendedBlock;
import org.apache.hadoop.hdfs.security.token.block.BlockTokenIdentifier;
import org.apache.hadoop.hdfs.server.datanode.CachingStrategy;
import org.apache.hadoop.hdfs.shortcircuit.ShortCircuitShm.SlotId;
import org.apache.hadoop.security.token.Token;
import org.apache.hadoop.util.DataChecksum;

/**
 * DataTransferProtocol是用来描述写入或者读出Datanode上数据的基于TCP的流式接口，
 * HDFS客户端与数据节点以及数据节点与数据节点之间的数据块传输输就是基于DataTransferProtocol接口实现的。
 *
 * DataTransferProtocol接口调用并没有使用Hadoop RPC框架提供的功能，因为Hadoop RPC框架的效率目前还不足以支撑超大文件的读写
 * 而是定义了用于发送DataTransferProtocol请求的Sender类， 以及用于响应DataTransferProtocol请求的 Receiver类，
 *
 * Sender类和Receiver类都实现了DataTransferProtocol接口。
 *
 * 我们假设DFSClient发起了一个 DataTransferProtocol.readBlock()操作，
 * 那么DFSClient会调用Sender将这个请求序列化，并传输给远端的Receiver。远端的Receiver接收到这个请求后，会反序列化请求，然后调用代码执行读取操作。
 */
@InterfaceAudience.Private
@InterfaceStability.Evolving
public interface DataTransferProtocol {
  public static final Log LOG = LogFactory.getLog(DataTransferProtocol.class);
  
  public static final int DATA_TRANSFER_VERSION = 28;

  /** 
   * 从当前Datanode读取指定的数据块。
   */
  public void readBlock(final ExtendedBlock blk,
      final Token<BlockTokenIdentifier> blockToken,
      final String clientName,
      final long blockOffset,
      final long length,
      final boolean sendChecksum,
      final CachingStrategy cachingStrategy) throws IOException;

  /**
   * 将指定数据块写入数据流管道(pipeLine)中
   */
  public void writeBlock(final ExtendedBlock blk,
      final StorageType storageType, 
      final Token<BlockTokenIdentifier> blockToken,
      final String clientName,
      final DatanodeInfo[] targets,
      final StorageType[] targetStorageTypes, 
      final DatanodeInfo source,
      final BlockConstructionStage stage,
      final int pipelineSize,
      final long minBytesRcvd,
      final long maxBytesRcvd,
      final long latestGenerationStamp,
      final DataChecksum requestedChecksum,
      final CachingStrategy cachingStrategy,
      final boolean allowLazyPersist) throws IOException;

  /**
   * 将指定数据块复制(transfer)到另一个Datanode上。
   *
   * 数据块复制操作是指数据流管道中的数据节点出现故障， 需要用新的数据节点替换异常的数据节点时，
   * DFSClient会调用这个方法将数据流管道中异常数据节点上已经写入的数据块复制到新添加的数据节点上。
   */
  public void transferBlock(final ExtendedBlock blk,
      final Token<BlockTokenIdentifier> blockToken,
      final String clientName,
      final DatanodeInfo[] targets,
      final StorageType[] targetStorageTypes) throws IOException;

  /**
   *  获取一个短路(short circuit)读取数据块的文件描述符
   */
  public void requestShortCircuitFds(final ExtendedBlock blk, final Token<BlockTokenIdentifier> blockToken,
      SlotId slotId, int maxVersion, boolean supportsReceiptVerification) throws IOException;

  /**
   * 释放一个短路读取数据块的文件描述符。
   */
  public void releaseShortCircuitFds(final SlotId slotId) throws IOException;

  /**
   * 获取保存短路读取数据块的共享内存
   */
  public void requestShortCircuitShm(String clientName) throws IOException;
  
  /**
   * 将从 源Datanode复制来的数据块写入本地Datanode。 写成功后通知NameNode，并且删除 源Datanode上的数据块。
   * 这个方法主要用在数据块平衡操作(balancing)的场景下。
   */
  public void replaceBlock(final ExtendedBlock blk,
      final StorageType storageType,
      final Token<BlockTokenIdentifier> blockToken,
      final String delHint,
      final DatanodeInfo source) throws IOException;

  /**
   * 复制当前Datanode上的数据块。这个方法主要用在数据块平衡操作的场景下。
   */
  public void copyBlock(final ExtendedBlock blk, final Token<BlockTokenIdentifier> blockToken) throws IOException;

  /**
   * 获取指定数据块的校验值
   */
  public void blockChecksum(final ExtendedBlock blk, final Token<BlockTokenIdentifier> blockToken) throws IOException;
}
